﻿<#
	File      = wmifx.psm1
	Author    = Kirk "Poshoholic" Munro
	Publisher = Poshoholic Studios
	Copyright = © 2012 Poshoholic Studios. All rights reserved.
#>

#region Enable strict mode.

Set-StrictMode -Version Latest

#endregion

#region Set the module export mode to explicit.

Export-ModuleMember

#endregion

#region Load the string table.

Import-LocalizedData -FileName StringTable -BindingVariable StringTable -UICulture en-US -ErrorAction SilentlyContinue
if ($host.CurrentUICulture.Name -ne 'en-US') {
	Import-LocalizedData -FileName StringTable -BindingVariable StringTable -ErrorAction SilentlyContinue
}

#endregion

Write-Progress -Activity $StringTable.ProgressOnImportTitle -Status $StringTable.ProgressOnImportMessageInitializing

#region Load any functions defined in ps1 files.

. $PSScriptRoot\ConvertTo-WmiFilter.ps1

#endregion

#region Load property adapters into the current PowerShell session if they aren't already loaded.

Write-Progress -Activity $StringTable.ProgressOnImportTitle -Status $StringTable.ProgressOnImportMessageLoadingPropertyAdapters
if (Test-Path -LiteralPath $PSScriptRoot\root) {
	Get-ChildItem -LiteralPath $PSScriptRoot\root -Recurse | Where-Object {$_.PSIsContainer} | ForEach-Object {
		#region Determine the next dll path.

		$dllPath = Join-Path -Path $_.FullName -ChildPath "$(Split-Path -Path $_.FullName -Leaf)PropertyAdapter.dll"
		$defaultDllPath = $dllPath
		$revision = 1
		while (Test-Path -LiteralPath $dllPath) {
			$dllPath = $defaultDllPath -replace '\.dll$', "${revision}.dll"
			$revision++
		}

		#endregion

		#region Compile and remove any .cs files that are found.

		if ($csFiles = @(Get-ChildItem -LiteralPath $_.FullName -Filter *.cs | Where-Object {$_.Extension -eq '.cs'} | Select-Object -ExpandProperty FullName)) {
			$assemblies = @(
				[System.Reflection.Assembly]::ReflectionOnlyLoad('System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089').Location
				[System.Reflection.Assembly]::ReflectionOnlyLoad('System.Management, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a').Location
				[System.Reflection.Assembly]::ReflectionOnlyLoad('System.Management.Automation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35').Location
			)
			$dictionary = New-Object -TypeName 'System.Collections.Generic.Dictionary[string,string]'
			$dictionary.Add('CompilerVersion','v2.0')
			$compilerParameters = New-Object -TypeName System.CodeDom.Compiler.CompilerParameters
			$compilerParameters.OutputAssembly = $dllPath
			$compilerParameters.ReferencedAssemblies.AddRange($assemblies)
			$compilerParameters.GenerateExecutable = $false
			$compilerParameters.GenerateInMemory = $false
			$compilerParameters.CompilerOptions = '/optimize'
			$cSharpCodeProvider = New-Object -TypeName Microsoft.CSharp.CSharpCodeProvider -ArgumentList $dictionary
			$results = $cSharpCodeProvider.CompileAssemblyFromFile($compilerParameters,[System.String[]]$csFiles)
			if ((Test-Path -LiteralPath $dllPath) -and ($results.Errors.Count -eq 0)) {
				Remove-Item -LiteralPath $csFiles
			} else {
				Write-Warning ($StringTable.WarningPropertyAdapterCompilationFailed -f $_.FullName)
				foreach ($errorMessage in $results.Errors) {
					Write-Debug $errorMessage
				}
			}
		}

		#endregion

		#region Load the PropertyAdapter dlls if there are any.

		$propertyAdapterDllSearchPath = Join-Path -Path $_.FullName -ChildPath "$(Split-Path -Path $_.FullName -Leaf)PropertyAdapter*.dll"
		if (Test-Path -Path $propertyAdapterDllSearchPath) {
			foreach ($propertyAdapterDllPath in Get-Item -Path $propertyAdapterDllSearchPath) {
				Add-Type -Path $propertyAdapterDllPath -ReferencedAssemblies System.Management
			}
		}

		#endregion
	}
}

#endregion

#region Import (prepend) type data extensions into the current PowerShell session.

<#
PowerShell 3.0 Breaking Change:
$Host.Runspace.RunspaceConfiguration is not used to store the type files that PowerShell
loads. These are now stored in $Host.Runspace.InitialSessionState.Types in PowerShell 3.0
and later. To work around this, script authors have to check the version and make the
appropriate call.
#>
Write-Progress -Activity $StringTable.ProgressOnImportTitle -Status $StringTable.ProgressOnImportMessageLoadingTypeExtensions
$typeFilesAlreadyLoaded = @()
if ($PSVersionTable.PSVersion -lt [System.Version]'3.0') {
	if ($Host.Runspace.RunspaceConfiguration -ne $null) {
		$typeFilesAlreadyLoaded = @($Host.Runspace.RunspaceConfiguration.Types | Select-Object -ExpandProperty FileName)
	}
} else {
	if ($Host.Runspace.InitialSessionState -ne $null) {
		$typeFilesAlreadyLoaded = @($Host.Runspace.InitialSessionState.Types | Select-Object -ExpandProperty FileName)
	}
}
if (Test-Path -LiteralPath $PSScriptRoot\root) {
	Get-ChildItem -LiteralPath $PSScriptRoot\root -Filter *.ps1xml -Recurse -Force | Where-Object {$_.Name -match '^types(\d+)?\.ps1xml$'} | ForEach-Object {
		if ($typeFilesAlreadyLoaded -notcontains $_.FullName) {
			$namespace = (($_.FullName | Split-Path -Parent) -replace ("^$PSScriptRoot\" -replace '\\','\\'))
			Write-Progress -Activity $StringTable.ProgressOnImportTitle -Status ($StringTable.ProgressOnImportMessageLoadingTypeExtension -f $namespace)
			Update-TypeData -PrependPath $_.FullName
			$typeFilesAlreadyLoaded += $_.FullName
		}
	}
}

#endregion

#region Load proxy functions that have been defined already.

Write-Progress -Activity $StringTable.ProgressOnImportTitle -Status $StringTable.ProgressOnImportMessageLoadingProxyFunctions
if (Test-Path -LiteralPath $PSScriptRoot\root) {
	Get-ChildItem -LiteralPath $PSScriptRoot\root -Filter *.ps1 -Recurse | Where-Object {$_.Extension -eq '.ps1'} | ForEach-Object {
		try {
			$namespace = (($_.FullName | Split-Path -Parent) -replace ("^$PSScriptRoot\" -replace '\\','\\'))
			Write-Progress -Activity $StringTable.ProgressOnImportTitle -Status ($StringTable.ProgressOnImportMessageLoadingProxyFunction -f $namespace, $($_.Name -replace '\.ps1$'))
			. $_.FullName
		} catch {
		}
	}
}

#endregion

Write-Progress -Activity $StringTable.ProgressOnImportTitle -Status $StringTable.ProgressOnImportMessageComplete -Complete

#region Clean-up the module when it is removed.

$ExecutionContext.SessionState.Module.OnRemove = {
	#region Remove type data files that were loaded by this module.

	<#
	PowerShell 3.0 Breaking Change:
	$Host.Runspace.RunspaceConfiguration is not used to store the type files that PowerShell
	loads. These are now stored in $Host.Runspace.InitialSessionState.Types in PowerShell 3.0
	and later. To work around this, script authors have to check the version and make the
	appropriate call.
	#>
	$typeDataRemoved = $false
	if ($PSVersionTable.PSVersion -lt [System.Version]'3.0') {
		if ($Host.Runspace.RunspaceConfiguration -ne $null) {
			$rootPath = Join-Path -Path $PSScriptRoot -ChildPath root
			for ($index = $Host.Runspace.RunspaceConfiguration.Types.Count - 1; $index -ge 0; $index--) {
				if ($Host.Runspace.RunspaceConfiguration.Types[$index].FileName.StartsWith($rootPath)) {
					$Host.Runspace.RunspaceConfiguration.Types.RemoveItem($index)
					$typeDataRemoved = $true
				}
			}
		}
	} else {
		if ($Host.Runspace.InitialSessionState -ne $null) {
			for ($index = $Host.Runspace.InitialSessionState.Types.Count - 1; $index -ge 0; $index--) {
				if ($Host.Runspace.InitialSessionState.Types[$index].FileName.StartsWith($PSScriptRoot)) {
					$Host.Runspace.InitialSessionState.Types.RemoveItem($index)
					$typeDataRemoved = $true
				}
			}
		}
	}
	if ($typeDataRemoved) {
		Update-TypeData
	}

	#endregion
}

#endregion